package com.rongji.dfish.base.util;

import java.text.DecimalFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;

import net.sourceforge.pinyin4j.PinyinHelper;

import com.rongji.dfish.base.Utils;

/**
 * 字符串的一些通用方法。
 * @author DFish Team
 *
 */
@SuppressWarnings("unchecked")
public class StringUtil {
	/**
	 * 对比汉字字符串 拼音顺序
	 * 
	 * @param s1
	 *            String
	 * @param s2
	 *            String
	 * @return int
	 */
	public static int chineseCompare(String s1, String s2) {
		return CHINESE_ORDER.compare(s1, s2);
	}
	
	/**
	 * 取得拼音
	 * @param str
	 * @param hasToneNumber 是否包含音调。音调以0(轻声)1(阴平)2(阳平)3(上声)4(去声)
	 * @return
	 */
	public static String getPinyin(String str, boolean hasToneNumber) {
		if(str==null)return null;
		StringBuilder sb=new StringBuilder();
		for(char c:str.toCharArray()){
			if(c>=32 && c<=127){
				sb.append(c);
				continue;
			}//英文字不做解析
			String[] pinyins=PinyinHelper.toHanyuPinyinStringArray(c);
			if(pinyins==null||pinyins.length==0){
				sb.append(c);
				continue;
			}
			if(hasToneNumber){
				sb.append(pinyins[0]);
			}else{
				String s=pinyins[0];
				char lastChar=s.charAt(s.length()-1);
				if(lastChar>='0'&&lastChar<='4'){
					sb.append(s.substring(0,s.length()-1));
				}else{
					sb.append(pinyins[0]);
				}
			}
		}
		return sb.toString();
	}
	/**
	 * 同时取得全拼和简拼的字符串
	 * @param str
	 * @param hasToneNumber
	 * @return
	 */
	public static String[] getPinyinFullShortFormat(String str, boolean hasToneNumber) {
		if(str==null)return null;
		StringBuilder sb=new StringBuilder();
		StringBuilder shortSB=new StringBuilder();
		for(char c:str.toCharArray()){
			if(c>=32 && c<=127){
				sb.append(c);
				shortSB.append(c);
				continue;
			}//英文字不做解析
			String[] pinyins=PinyinHelper.toHanyuPinyinStringArray(c);
			if(pinyins==null||pinyins.length==0){
				sb.append(c);
				shortSB.append(c);
				continue;
			}
			shortSB.append(pinyins[0].charAt(0));
			if(hasToneNumber){
				sb.append(pinyins[0]);
			}else{
				String s=pinyins[0];
				char lastChar=s.charAt(s.length()-1);
				if(lastChar>='0'&&lastChar<='4'){
					sb.append(s.substring(0,s.length()-1));
				}else{
					sb.append(pinyins[0]);
				}
			}
		}
		return new String[]{sb.toString(),shortSB.toString()};
	}
	/**
	 * 排序时 不区分大小写的英文对比器
	 */
	public static final Comparator<String> CASE_INSENSITIVE_ORDER = String.CASE_INSENSITIVE_ORDER;
	/**
	 * 排序时候 中文对比器java.text.Collator
	 * @deprecated java.text.Collator的中文排序不是很准确 
	 */
	@Deprecated
	public static final Comparator<Object> CHINESE_ORDER_JDK = java.text.Collator.getInstance(java.util.Locale.SIMPLIFIED_CHINESE);


	/**
	 * 字符串的排序器，和{@link #CHINESE_ORDER_JDK}不同。
	 * 该排序方式对全角半角不影响排序结果。对英文字符大小写不敏感。
	 * 并且和windows一样，会对数字进行分析。
	 * 新建文本文档 (9).txt 和 新建文本文档 (10).txt 会以自然语言的方式进行排序。
	 * 并且一般来说，符号会在文本的前面，而不会像ASCII那样分散在字符中间。
	 * 字库合并了简体与繁体中文。
	 */
	public static java.util.Comparator<Object> CHINESE_ORDER=null;
	static{
		String[] chineseOrderProviders={"com.rongji.dfish.misc.chinese.ChineseOrder"};
		for(String prov:chineseOrderProviders){
			try{
				Class<?> c=Class.forName(prov);
				CHINESE_ORDER=(Comparator<Object>) c.newInstance();
			}catch(Exception ex){
				ex.printStackTrace();
			}
		}
		if(CHINESE_ORDER==null){
			CHINESE_ORDER=java.text.Collator.getInstance(Locale.SIMPLIFIED_CHINESE);
		}
	}

	/**
	 * 截取字符串content前面size个字节的内容 汉字相当于2个英文,数字相当于1个英文，每个英文字母占一个字节空间 要保证汉字不被错误分割 by
	 * YeJL 2006-08
	 * 
	 * @param content
	 *            String 将要截取的源字符串
	 * @param size
	 *            int 截取字符数
	 * @return String
	 */
	public static String shortenString(String content, int size) {
		return shortenString(content, size, 2, "...");
	}

	/**
	 * 截取字符串content前面size个字节的内容 汉字相当于chineseCharSize个英文,数字相当于1个英文，每个英文字母占一个字节空间
	 * 要保证汉字不被错误分割
	 * 
	 * @todo 英文单词，如果可能最好也不要被分割。 by YeJL 2006-08
	 * @param content
	 *            String 将要截取的源字符串
	 * @param limitSize
	 *            int 截取字符数
	 * @param chineseCharSize
	 *            汉字相当于chineseCharSize个字节，一般是GBK为2，IE显示也占2个空间。 UTF8下显示空间还是2
	 *            但字节数为3， GB18030下字节数为4
	 * @param replacePostfix
	 *            被截取的字符用该字符代替
	 * @return String
	 */
	public static String shortenString(String content, int limitSize, int chineseCharSize,
			String replacePostfix) {
		if (content == null || content.equals("") || limitSize == 0) {
			return "";
		}
		if (content.getBytes().length <= limitSize) {
			return content;
		}

		long l = 0;
		StringBuilder sb = new StringBuilder();
		char[] c = content.toCharArray();
		for (int i = 0; i < c.length; i++) {
			char ca = c[i];

			sb.append(ca);
			int ascii = ca;
			if (ascii < 0 || ascii > 255) {
				l += chineseCharSize;
			} else {
				l += 1;
			}
			if (l > (limitSize - replacePostfix.length())) {
				sb.deleteCharAt(sb.length() - 1).append(replacePostfix);
				break;
			}
		}
		return sb.toString();
	}

	private static Random r = new Random(20080815L + System.currentTimeMillis());

	/**
	 * 生成一个指定长度的随机字符串
	 * 
	 * @param length
	 *            int 指定长度
	 * @param containNum
	 *            boolean 是否包含数字
	 * @param containLowcase
	 *            boolean 是否小写数字
	 * @param containUppcase
	 *            boolean 是否包含大写字母
	 * @param containSymbol
	 *            boolean 是否包含符号
	 * @return String
	 */
	public static String getRadomString(int length, boolean containNum, boolean containLowcase,
			boolean containUppcase, boolean containSymbol) {
		if (!containNum && !containLowcase && !containUppcase && !containSymbol) {
			return "";
		}
		StringBuffer sb = new StringBuffer(length);
		if (0 < length) {
			while (sb.length() < length) {
				byte[] can = new byte[length * 2];
				r.nextBytes(can);
				for (int i = 0; i < can.length; i++) {
					if ((containNum && can[i] >= '0' && can[i] <= '9')
						|| (containLowcase && can[i] >= 'a' && can[i] <= 'z')
						|| (containUppcase && can[i] >= 'A' && can[i] <= 'Z')
						|| (containSymbol && 
								((can[i] >= '!' && can[i] <= '/')
								|| (can[i] >= ':' && can[i] <= '@')
								|| (can[i] >= '[' && can[i] <= '`')
								|| (can[i] >= '{' && can[i] <= '~'))
						)) {
						sb.append((char) can[i]);
						if (sb.length() >= length) {
							break;
						}
					}
				}
			}
			return sb.toString();
		}
		return "";
	}
	/**
	 * 判断两个字符串是否equals
	 * 空字符串也可以对比。另外把 null和""当成是一样的。
	 * @param str1
	 * @param str2
	 * @return
	 */
	public static boolean nullAbleEquals(String str1, String str2) {
		if (str1 == null || str1.equals("")) {
			return (str1 == null || str1.equals(""));
		} else {
			return str1.equals(str2);
		}
	}
	/**
	 * 将数值金额人民币转化为中文意思
	 * @param dblNum
	 * @return
	 */
	public static String RMBtoCHINESE(String dblNum) {
		try {
			if (dblNum.equals(""))
				return null;
			String sNum = "零壹贰叁肆伍陆柒捌玖负";
			String sUni = "整分角元拾佰仟万拾佰仟亿拾佰仟万拾佰仟亿拾佰仟万拾佰仟";
			int L = dblNum.length();
			if (dblNum.lastIndexOf(".") == -1) {//整数转化标准人民币格式
				dblNum = dblNum + ".00";
			} else {
				if (dblNum.lastIndexOf(".") + 1 == L) {//补齐标准人民币格式
					dblNum = dblNum + "00";
				}
				if (dblNum.lastIndexOf(".") + 2 == L) {//补齐标准人民币格式
					dblNum = dblNum + "0";
				}
			}
			L = dblNum.length();
			if (L > 27)
				return "数值溢出"; // 包括小数点和2位小数
			StringBuffer sN = new StringBuffer(); // 转换前的字符串
			sN.append(dblNum);
			sN.deleteCharAt(L - 3); // 删除小数点
			--L;
			StringBuffer sT = new StringBuffer(); // 转换后的字符串
			if (sN.charAt(L - 1) == '0' && sN.charAt(L - 2)== '0')
				sT.insert(0, '整'); // 分位为零则尾部加'整'
			boolean Ziro = false, a = false, b = false;
			int n = 0;
			for (int i = 1; i <= L; i = i + 1) {
				n = sN.charAt(L - i) - '0'; // 数值
				if (i == 7 || i == 15)
					b = n == 0; // 万位、万亿位是零
				if ((i > 7) & (i < 11) || (i > 15) & (i < 19))
					b = (n == 0) & b; // 万-亿位、或亿-亿位之间全是零
				a = (i == 1 || i == 3 || i == 7 || i == 11 || i == 15 || i == 19 || i == 23); // 万亿、亿、万、元、分位为0时不写'零'
				if (n > 0 || (i > 1 & a)) {
					if ((i == 11 || i == 19) & b)
						sT.setCharAt(0, '亿'); // 用'亿'覆盖'万'
					else
						sT.insert(0, sUni.charAt(i)); // 插入单位
				}
				if (!(n == 0 & (Ziro || a)))
					sT.insert(0, sNum.charAt(n)); // 插入数值
				Ziro = n == 0;
			}
			if (n == 0)
				sT.insert(0, '零');
			return sT.toString();
		} catch (Exception e) {
			return "格式不正确";
		}
	}
	/**
	 * 替换EL表达式的内容
	 * 表达式中的内容将通过getter 转成需要的内容
	 * @param elStr
	 * @param getter
	 * @return
	 */
	public static String relaceELTag(String elStr,ELcontentGetter getter){
		if(elStr==null)return elStr;
		char[] cs=elStr.toCharArray();
		//找到所有的${...}然后把内容用getter替换
		List<Integer[]> matchs=new ArrayList<Integer[]>();
		int start=-1;
		int end=-1;
		for(int i=0;i<cs.length;i++){
			if(start<0){
				if(cs[i]=='$'){
					if(i+1<cs.length){
						if(cs[i+1]=='{'){
							start=i;
						}
					}
				}
			} else{
				if(cs[i]=='}'){
					end=i;
					matchs.add(new Integer[]{start,end});
					start=-1;
					end=-1;
				}
			}
		}
		start=0;
		end=elStr.length();
	
		StringBuilder sb=new StringBuilder();
		for(Integer[] match:matchs){
			sb.append(cs,start,match[0]-start);
			sb.append(getter.getCotent(elStr.substring(match[0]+2, match[1])));
			start=match[1]+1;
		}
		sb.append(cs,start,end-start);
		return sb.toString();
	}

	public static interface ELcontentGetter{
		public String getCotent(String tag);
	}
	
	/**
	 * 日期格式化模板
	 */
	private static Map<String, Format> datePatterns = new HashMap<String, Format>();
	/**
	 * 数值格式化模板
	 */
	private static Map<String, Format> numberPatterns = new HashMap<String, Format>();
	
	/**
	 * 将数据格式化成字符(目前仅提供日期和数值的格式化)
	 * @param data 数据对象
	 * @param pattern 格式样式
	 * @return
	 */
	public static String format(Object data, String pattern) {
		if (data == null) {
			return "";
		}
		if (Utils.isEmpty(pattern)) {
			if (data instanceof String) {
				return (String) data;
			} else {
				// 这里是否返回data.toString();
				throw new IllegalArgumentException(data.getClass().getName() + " can not be formatted by the empty pattern." );
			}
		}
		Format f = null;
		if (data instanceof Date) {
			f = datePatterns.get(pattern);
			if (f == null) {
				f = new SimpleDateFormat(pattern);
				datePatterns.put(pattern, f);
			}
		} else if (data instanceof Number) {
			f = numberPatterns.get(pattern);
			if (f == null) {
				f = new DecimalFormat(pattern);
				numberPatterns.put(pattern, f);
			}
		} else {
			throw new IllegalArgumentException(data.getClass().getName() + " can not be formatted by the pattern(" + pattern + ")." );
		}
		synchronized(f){
			return f.format(data);
		}
	}
	
	/**
	 * 获取高亮的字符串，默认高亮的关键字以&lt;em&gt;&lt;/em&gt;标签高亮
	 * @param source 源字符串
	 * @param key 高亮的关键字
	 * @return String 高亮的字符串
	 */
	public static String highlight(String source,String key){
		return highlight(source,key,new HighlightParam());
	}
	
	/**
	 * 获取高亮的字符串
	 * @param source 源字符串
	 * @param key 高亮的关键字
	 * @param param 高亮参数
	 * @return String 高亮的字符串
	 */
	public static String highlight(String source,String key,HighlightParam param){
		if(source==null||source.equals("")||key==null||key.equals("")){
			return source;
		}
		boolean[] matchs=new boolean[source.length()];
		//找到每个词在字符串中的起始位置，将相应数组位置变为true
		int index = 0;
		for(String word:splitKeyword(key,param.getMinMatch())){
    		while ((index = source.indexOf(word, index)) != -1) {
    			for(int i=index;i<index+word.length();i++){
    				matchs[i]=true;
    			}
    			index++;
    		}
    	}
		StringBuilder lightStr=new StringBuilder();
		char[] srcarr=source.toCharArray();
		boolean isInsert=false;
		for(int i=0;i<srcarr.length;i++){
			if(isInsert){
				if(!matchs[i]){
					lightStr.append(param.getPostfix());
					isInsert=false;
				}
			}else{
				if(matchs[i]){
					lightStr.append(param.getPrefix());
					isInsert=true;
				}
			}
			lightStr.append(srcarr[i]);
		}
		if(isInsert){
			lightStr.append(param.postfix);
		}
		return lightStr.toString();
	}
	/**
	 * 将高亮的关键字拆分
	 * @param keyWord 关键字
	 * @param matchLength 关键字拆分长度
	 * @return List&lt;String&gt;
	 */
	private static List<String> splitKeyword(String keyWord, int matchLength){
		if (Utils.isEmpty(keyWord)) {
			return Collections.emptyList();
		}
		List<String> splitWords=new ArrayList<String>();
		if (matchLength <= 0) { // 全字匹配
			splitWords.add(keyWord);
		} else {
			// 按照给出的长度值进行匹配
			for(int i=0;i<keyWord.length()-matchLength+1;i++){
				String word=keyWord.substring(i,i+matchLength);
				splitWords.add(word);
			}
		}
		return splitWords;
	}
	
	/**
	 * 关键字高亮参数
	 * 
	 * @author DFish Team
	 *
	 */
	public static class HighlightParam{
		/**
		 * 高亮标签前缀
		 */
		private String prefix="<em>";
		/**
		 * 高亮标签后缀
		 */
		private String postfix="</em>";
		/**
		 * 高亮关键字匹配长度;&lt;=0:全字匹配,其他情况按照长度拆分关键字再进行匹配
		 */
		private int minMatch;
		
		public String getPrefix() {
			return prefix;
		}
		public void setPrefix(String prefix) {
			this.prefix = prefix;
		}
		public String getPostfix() {
			return postfix;
		}
		public void setPostfix(String postfix) {
			this.postfix = postfix;
		}
		public int getMinMatch() {
			return minMatch;
		}
		public void setMinMatch(int minMatch) {
			this.minMatch = minMatch;
		}
		
	}
}
